package jwt

import (
	"errors"
	"time"

	"github.com/golang-jwt/jwt/v5"
)

// 定义错误
var (
	ErrTokenExpired     = errors.New("令牌已过期")
	ErrTokenNotValidYet = errors.New("令牌尚未生效")
	ErrTokenMalformed   = errors.New("令牌格式错误")
	ErrTokenInvalid     = errors.New("无法处理此令牌")
)

// 全局TokenGuard实例
var DefaultGuard *TokenGuard

// CustomClaims 自定义JWT声明结构
type CustomClaims struct {
	ID       uint   `json:"id"`
	Username string `json:"username"`
	RoleID   uint   `json:"role_id"`
	jwt.RegisteredClaims
}

// Config JWT配置结构
type Config struct {
	SigningKey  string        // 签名密钥
	ExpiresTime time.Duration // 过期时间
	Issuer      string        // 签发人
}

// TokenGuard JWT操作结构
type TokenGuard struct {
	Config Config
}

// NewTokenGuard 创建一个新的JWT处理实例
func NewTokenGuard(config Config) *TokenGuard {
	return &TokenGuard{
		Config: config,
	}
}

// GenerateToken 为用户生成Token
func GenerateToken(userID uint, username string) (string, error) {
	if DefaultGuard == nil {
		return "", errors.New("默认TokenGuard未初始化")
	}

	claims := CustomClaims{
		ID:       userID,
		Username: username,
	}

	return DefaultGuard.CreateToken(claims)
}

// CreateToken 创建一个新的Token
func (guard *TokenGuard) CreateToken(claims CustomClaims) (string, error) {
	// 设置注册声明
	claims.RegisteredClaims = jwt.RegisteredClaims{
		ExpiresAt: jwt.NewNumericDate(time.Now().Add(guard.Config.ExpiresTime)),
		IssuedAt:  jwt.NewNumericDate(time.Now()),
		NotBefore: jwt.NewNumericDate(time.Now()),
		Issuer:    guard.Config.Issuer,
	}

	// 创建token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return token.SignedString([]byte(guard.Config.SigningKey))
}

// ParseToken 解析Token
func (guard *TokenGuard) ParseToken(tokenString string) (*CustomClaims, error) {
	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(guard.Config.SigningKey), nil
	})

	if err != nil {
		// v5版本的错误处理方式不同，使用errors.Is判断错误类型
		switch {
		case errors.Is(err, jwt.ErrTokenMalformed):
			return nil, ErrTokenMalformed
		case errors.Is(err, jwt.ErrTokenExpired) || errors.Is(err, jwt.ErrTokenSignatureInvalid):
			return nil, ErrTokenExpired
		case errors.Is(err, jwt.ErrTokenNotValidYet):
			return nil, ErrTokenNotValidYet
		default:
			return nil, ErrTokenInvalid
		}
	}

	// 验证token
	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, ErrTokenInvalid
}

// RefreshToken 刷新Token
func (guard *TokenGuard) RefreshToken(tokenString string) (string, error) {
	// 解析旧token
	_, err := guard.ParseToken(tokenString)
	if err != nil && errors.Is(err, ErrTokenExpired) {
		// 如果是因为过期而失败，我们仍然可以刷新
		token, _ := jwt.ParseWithClaims(tokenString, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
			return []byte(guard.Config.SigningKey), nil
		})

		if claims, ok := token.Claims.(*CustomClaims); ok {
			// 颁发新token
			return guard.CreateToken(*claims)
		}
	}
	return "", err
}
